/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.server.servlet;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Properties;

/**
 * This class implements the export/import manager, which can be used for file uploads and downloads. It
 * manages the data, which will be stored in temporary files. 
 * 
 * The main purpose of the manager is to reduce the memory footprint of the server. It allows to upload or download in
 * fragments, i.e. multiple calls to upload/download part of the data.
 * 
 * @author Walter Luetgenau
 */
public class ExportImportManager {
    static ExportImportManager instance = null;
    HashMap<String,Data> dataCache = new HashMap<String,Data>();
    int maxId;
    
    class Data {
        File file;
        OutputStream outputStream;
        InputStream inputStream;
        RepositoryWriter writer;
        
        public Data() throws IOException {
            file = File.createTempFile("expImp", "");
        }

        public File getFile() {
            return file;
        }

        public InputStream getInputStream() {
            return inputStream;
        }

        public void setInputStream(InputStream inputStream) {
            this.inputStream = inputStream;
        }

        public OutputStream getOutputStream() {
            return outputStream;
        }

        public void setOutputStream(OutputStream outputStream) {
            this.outputStream = outputStream;
        }

        public RepositoryWriter getWriter() {
            return writer;
        }

        public void setWriter(RepositoryWriter writer) {
            this.writer = writer;
        }

        public long getSize() {
            return file.length();
        }

    }
    
    private ExportImportManager() {        
    }
    
    public static ExportImportManager getInstance() {
        if (instance == null) {
            synchronized(ExportImportManager.class) {
                instance = new ExportImportManager();
            }
        }
        
        return instance;           
    }
    
    public String openData() throws IOException {
        synchronized(this) {
            maxId++;
            String id = "" + maxId;
            dataCache.put(id, new Data());
            
            return id;
        }
    }
    
    public OutputStream getOutputStream(String id) throws FileNotFoundException, IOException {
        Data data = dataCache.get(id);
        OutputStream out = data.getOutputStream();
        if (out == null) {
            if (data.getInputStream() != null) {
                data.getInputStream().close();
                data.setInputStream(null);
            }
            out = new FileOutputStream(data.getFile());
            data.setOutputStream(out);
        }
        
        return out;
    }
    
    public void writeData(String id, byte[] data) throws FileNotFoundException, IOException {
        OutputStream out = getOutputStream(id);
        out.write(data);
    }
    
    public InputStream getInputStream(String id) throws FileNotFoundException, IOException {
        Data data = dataCache.get(id);
        InputStream in = data.getInputStream();
        if (in == null) {
            if (data.getOutputStream() != null) {
                data.getOutputStream().close();
                data.setOutputStream(null);
            }
            in = new FileInputStream(dataCache.get(id).getFile());
            data.setInputStream(in);
        }
        
        return in;
    }
    
    public byte[] readData(String id, int maxLength) throws FileNotFoundException, IOException {
        InputStream in = getInputStream(id);
        
        int count = Math.min(maxLength, in.available());
        byte[] buf = new byte[count];
        in.read(buf);
        
        return buf;
    }

    public long getSize(String id) {
        Data data = dataCache.get(id);
        if (data == null)
            return 0;

        return data.getSize();
    }
    
    public boolean moreDataAvailable(String id) throws FileNotFoundException, IOException {
        return getInputStream(id).available() > 0;
    }
    
    public void closeData(String id) throws IOException {
        Data data = dataCache.get(id);
        
        if (data == null)
            return;
        
        if (data.getInputStream() != null)
            data.getInputStream().close();
        if (data.getOutputStream() != null)
            data.getOutputStream().close();
        if (data.getFile() != null)
            data.getFile().delete();
        
        dataCache.remove(id);
    }
    
    public void setWriter(String id, RepositoryWriter writer) {
        dataCache.get(id).setWriter(writer);
    }
    
    public RepositoryWriter getWriter(String id) {
        return dataCache.get(id).getWriter();
    }
    
    public interface RepositoryWriter {
        public void write();
    }
}
